/*
* Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { X2DBeta, X3DFast } = require("../../engine/graphics/X2DInterface");
const { XMat4 } = require("../../engine/graphics/XMat4");
const { XMultlineText } = require("../../engine/graphics/XMultlineText");
const { XTexture } = require("../../engine/graphics/XTexture");
const { Scr } = require("../../engine/XDefine");
const { LogManager } = require("../ros1/LogManager");

class RosTexture {
    constructor(width, height, bpp = 3) {
        this.width = width;
        this.height = height;
        this.bpp = bpp;
        this.rid = XTexture.createTexture(width, height, bpp);
        this.tex = XTexture.ximages[this.rid]["tex"];

    }
    update(data) {
        gl.bindTexture(gl.TEXTURE_2D, this.tex);
        if (this.bpp == 3) {
            gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, this.width, this.height, gl.RGB, gl.UNSIGNED_BYTE, data);
        }
        else {
            gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, this.width, this.height, gl.RGBA, gl.UNSIGNED_BYTE, data);
        }
    }
    draw(x, y, scale) {
        let cid = XTexture.TmpCut(this.rid);
        X2DBeta.drawCut(cid, x, y, scale, scale, 0, 0, 0, 0xffffffff);
    }
}
class TextureManager {
    static texturePool = [];
    static getTexture(width, height, bpp = 3) {
        for (let i = 0; i < TextureManager.texturePool.length; i++) {
            if (TextureManager.texturePool[i].width == width && TextureManager.texturePool[i].height == height && TextureManager.texturePool[i].bpp == bpp) {
                return TextureManager.texturePool[i];
            }
        }
        let texture = new RosTexture(width, height, bpp);
        TextureManager.texturePool.push(texture);
        return texture;
    }
}

async function jpgToRgba(data, cache) {
    // console.log("data len:", data.length);
    const blob = new Blob([data], { type: 'image/jpeg' });
    const imageBitmap = await createImageBitmap(blob);

    let canvas = document.createElement("canvas");
    let ctx = canvas.getContext('2d');
    canvas.width = imageBitmap.width;
    canvas.height = imageBitmap.height;
    ctx.drawImage(imageBitmap, 0, 0);

    let ret = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
    cache.push([canvas.width, canvas.height, ret]);
    // console.log(ret[0],ret[1],ret[2],ret[3])
}

window.render = {
    OFFX: 0,
    OFFY: 0,
    MAXW: 0,
    MAXH: 0,
    ASYNC_DATA: {},
    reset: (ox, oy, nw, ad) => {
        render.OFFX = ox;
        render.OFFY = oy;
        render.MAXW = nw;
        render.MAXH = 0;
        render.ASYNC_DATA = ad;
    },
    text: (text, color = 0xffffffff) => {
        let w = X2DBeta.getTextWidth(text, 12);
        // X2DBeta.fillRect(render.OFFX, render.OFFY + render.MAXH, w, 16, 0xff404040);
        X2DBeta.drawText(text, 12, render.OFFX, render.OFFY + render.MAXH + 8, 1, 1, 0, 0, -2, color);
        render.MAXW = Math.max(render.MAXW, w);
        render.MAXH += 16;
    },
    imageRgb: (dataRgb, width, height, scale = 1) => {
        let texture = TextureManager.getTexture(width, height, 3);
        texture.update(dataRgb);
        texture.draw(render.OFFX, render.OFFY + render.MAXH, scale);
        render.MAXW = Math.max(render.MAXW, width * scale);
        render.MAXH += height * scale;
    },
    imageJpg: (dataJpg, scale = 1) => {
        if (!("jpg" in render.ASYNC_DATA)) {
            render.ASYNC_DATA["jpg"] = [];
        }
        jpgToRgba(dataJpg, render.ASYNC_DATA["jpg"]);
        if (render.ASYNC_DATA["jpg"].length == 0) return;
        while (render.ASYNC_DATA["jpg"].length > 1) {
            render.ASYNC_DATA["jpg"].shift();
        }
        let w = render.ASYNC_DATA["jpg"][0][0];
        let h = render.ASYNC_DATA["jpg"][0][1];
        let data = render.ASYNC_DATA["jpg"][0][2];
        // console.log(data[0], data[1], data[2], data[3])

        let texture = TextureManager.getTexture(w, h, 4);
        texture.update(data);
        texture.draw(render.OFFX, render.OFFY + render.MAXH, scale);
        render.MAXW = Math.max(render.MAXW, w * scale);
        render.MAXH += h * scale;
    },
    points2d: (points) => {
        let minx = 1000000, miny = 1000000, maxx = -1000000, maxy = -1000000;
        for (let p of points) {
            if (p[0] < minx) minx = p[0];
            if (p[0] > maxx) maxx = p[0];
            if (-p[1] < miny) miny = -p[1];
            if (-p[1] > maxy) maxy = -p[1];
        }
        let w = maxx - minx;
        let h = maxy - miny;
        let ox = render.OFFX - minx;
        let oy = render.OFFY - miny + render.MAXH;
        for (let p of points) {
            if (p.length < 3) {
                X2DBeta.fillRect(ox + p[0], oy - p[1], 1, 1, 0xffffffff);
            } else if (p.length < 4) {
                X2DBeta.fillRect(ox + p[0], oy - p[1], 1, 1, p[2]);
            } else if (p.length < 5) {
                X2DBeta.fillRect(ox + p[0] - p[3] / 2, oy - p[1] - p[3] / 2, p[3], p[3], p[2]);
            }
        }
        render.MAXW = Math.max(render.MAXW, w);
        render.MAXH += h;
    },
    points3d: (points, scale = 1, isOrtho = true) => {
        let minx = 1000000, miny = 1000000, minz = 1000000;
        let maxx = -1000000, maxy = -1000000, maxz = -1000000;
        for (let i = 0; i < points.length; i += 3) {
            let x = points[i], y = points[i + 1], z = points[i + 2];
            if (x < minx) minx = x;
            if (x > maxx) maxx = x;
            if (y < miny) miny = y;
            if (y > maxy) maxy = y;
            if (z < minz) minz = z;
            if (z > maxz) maxz = z;
        }
        minx *= scale;
        miny *= scale;
        minz *= scale;
        maxx *= scale;
        maxy *= scale;
        maxz *= scale;
        let w = maxx - minx;
        let h = maxz - minz;
        X3DFast.swapMode3D(isOrtho);
        let tmat = new XMat4();
        tmat.Unit();
        tmat.Scale(scale, scale, scale);
        // tmat.Rotate(90, 0, 0);
        tmat.Move(-Scr.logicw / 2 + render.OFFX - minx, 0, Scr.logich / 2 - render.OFFY - render.MAXH - maxz);
        X3DFast.drawPoints(points, tmat, 0xffffffff);
        X2DBeta.swapMode2D();
        render.MAXW = Math.max(render.MAXW, w + 1);
        render.MAXH += h + 1;
    },
    log: (...args) => {
        if (!render.ASYNC_DATA["changed"]) {
            return;
        }
        if (!("log" in render.ASYNC_DATA)) {
            render.ASYNC_DATA["log"] = [];
        }
        let ss = ""
        let cache = render.ASYNC_DATA["log"];
        for (let i = 0; i < args.length; i++) {
            let s;
            if (typeof args[i] === "object") {
                s = JSON.stringify(args[i]);
            } else {
                s = "" + args[i];
            }
            if (ss.length > 0) ss += "  ";
            ss += s;
        }
        ss.split("\n").forEach(line => {
            if (line.length > 256) {
                line = line.substring(0, 256) + "...";
            }
            cache.push(line);
        });
        while (cache.length > 8) {
            cache.shift();
        }
        // LogManager.I(...args);
    },
    log_flush: () => {
        if (!("log" in render.ASYNC_DATA)) {
            return;
        }
        let logs = render.ASYNC_DATA["log"];
        let mh = 0;
        for (let i = logs.length - 1; i >= 0; i--) {
            mh += XMultlineText.height(logs[i], 14, render.MAXW);
        }
        mh = Math.min(mh, 14 * 8);
        let offy = render.OFFY + render.MAXH + mh;
        for (let i = logs.length - 1; i >= 0; i--) {
            let h = XMultlineText.height(logs[i], 14, render.MAXW);
            offy -= h;
            if (offy < render.OFFY + render.MAXH) {
                break;
            }
            XMultlineText.draw(logs[i], 14, render.MAXW, render.OFFX, offy, 1, 1, 0, 0, 0, 0xffe0e0e0);
        }
        render.MAXH += mh;
    }
}